use crate::bootstrap::AppState;
use axum::body::Body;

use axum::extract::Request;
use axum::routing::any;
use axum::{extract::OriginalUri, http::StatusCode, response::IntoResponse, Router};
use resp::RespVO;
use users::users_router;
use utoipa_swagger_ui::SwaggerUi;
mod admin;
pub mod common;
pub mod errors;
pub mod resp;
mod users;

use admin::{admin_router, AdminApi};

use utoipa::{
    openapi::security::{ApiKey, ApiKeyValue, SecurityScheme},
    Modify, OpenApi,
};
use utoipa_axum::router::OpenApiRouter;
// use utoipa_rapidoc::RapiDoc;
use utoipa_redoc::{Redoc, Servable};
// use utoipa_scalar::{Scalar, Servable as ScalarServable};

struct SecurityAddon;

impl Modify for SecurityAddon {
    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
        if let Some(schema) = openapi.components.as_mut() {
            schema.add_security_scheme(
                "Authorization",
                SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("Authorization"))),
            )
        }
    }
}

#[derive(OpenApi)]
#[openapi(
        modifiers(&SecurityAddon),

        nest(
            (path = "/admin", api = AdminApi)
        )
)]

struct ApiDoc;

pub async fn create_routes(state: AppState) -> Router<AppState> {
    let (router, api) = OpenApiRouter::with_openapi(ApiDoc::openapi())
        .nest("/user", users_router(&state).into())
        .nest("/admin", admin_router(&state).into())
        .nest("/common", common::router(state).into())
        .split_for_parts();

    let router = router
        .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", api.clone()))
        .merge(Redoc::with_url("/redoc", api.clone()))
        .fallback(any(handler_404));

    router
}

#[axum::debug_handler]
async fn handler_404(uri: OriginalUri, resp: Request<Body>) -> impl IntoResponse {
    let error_url = uri.to_string();
    tracing::error!(" handler_404 : {} ", error_url);
    RespVO::<()>::from_error(
        Some(StatusCode::NOT_FOUND.as_u16()),
        format!(
            "URL {} The requested resource was not found, method: {}",
            error_url.as_str(),
            resp.method()
        )
        .to_string(),
        None,
    )
}
